import { Point } from './../model';
import { Vector2, Vector2Util } from './vector2.util';


export class PointUtil {

    constructor() {

    }

    /**
     * get point is inside area (if border is odd is inside, else if is even is not inside)
     * @param point current point
     * @param polygonPoints all border point list
     * @returns true is inside area, false is not inside
     */
    public static getPointIsInsideArea(point: Point, points: Point[]): boolean {
        const totalVectorPoint: Array<[Point, Point]> = [];
        const { x, y } = point;
        let isInside = false;
        for (let i = 0, length = points.length; i < length; i++) {
            const j = i + 1;
            const { x: x1, y: y1 } = points[i];
            const endPoint = points[j] || points[0];
            const { x: x2, y: y2 } = endPoint;
            if ((y1 - y) * (y2 - y) > 0 || (x1 === x2 && y1 === y2)) {
                continue;
            }
            const vectorStatus = Vector2Util.crossMultiply(new Vector2(x2 - x1, y2 - y1), new Vector2(x - x1, y - y1))
                * (y1 - y2) / (Math.abs(y2 - y1) || 1);
            // x > 0 point at right, x < 0 at left, x = 0 in line
            if (vectorStatus > 0) {
                continue;
            }
            if (vectorStatus === 0 && x <= Math.max(x1, x2) && x >= Math.min(x1, x2)) {
                isInside = true;
                break;
            }
            totalVectorPoint.push([points[i], endPoint]);
        }
        const crossingCount = totalVectorPoint.length;
        const filterPoint = totalVectorPoint.filter(item => (item[0].y === y || item[1].y === y));
        if (!filterPoint.length || isInside) {
            return isInside || crossingCount % 2 === 1;
        }
        let count = crossingCount - filterPoint.length;
        const [point0Start, point0End] = filterPoint[0];
        const [point1Start, point1End] = filterPoint[1];
        if (point0Start.y === point0End.y) {
            filterPoint.unshift(filterPoint.pop()!);
        } else if (point0End.x !== point1Start.x && point0End.y !== point1Start.y) {
            filterPoint.push(filterPoint.shift()!);
        }
        const doublePoints = filterPoint.filter(([pointStart, pointEnd]) => (pointStart.y !== pointEnd.y));
        for (let pIndex = 0, pointLength = doublePoints.length; pIndex < pointLength; pIndex += 2) {
            const nIndex = pIndex + 1;
            if ((doublePoints[pIndex][0].y - y) * (doublePoints[nIndex][1].y - y) < 0) {
                count++;
            }
        }
        return count % 2 === 1;
    }

    public static getControlRect(points: Point[]) {
        const xPoints = points.map(point => point.x);
        const yPoints = points.map(point => point.y);
        const xMin = Math.min.apply(null, xPoints);
        const xMax = Math.max.apply(null, xPoints);
        const yMin = Math.min.apply(null, yPoints);
        const yMax = Math.max.apply(null, yPoints);
        return {
            x: xMin,
            y: yMin,
            width: xMax - xMin,
            height: yMax - yMin
        };
    }

    public static getCircleRadius(point: Point, pointr: Point) {
        const xabs = Math.abs(point.x - pointr.x);
        const yabs = Math.abs(point.y - pointr.y);
        return Math.sqrt(Math.pow(xabs, 2) + Math.pow(yabs, 2));
    }
}
